﻿// Copyright (c) 2012, Alexander Endris
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#region Usings

using System.IO;

#endregion

namespace NamelessOne.Imaging.IO
{
  /// <summary>
  ///   A Binary reader that is capable of reading both little and big endian
  /// </summary>
  internal class BinaryReader : System.IO.BinaryReader
  {
    #region Public / Attributes

    public bool BigEndian
    {
      get { return bigEndian; }
      set { bigEndian = value; }
    }

    public long Position
    {
      get { return BaseStream.Position; }
    }

    #endregion

    #region Public / Constructors

    public BinaryReader(Stream stream)
      : base(stream)
    {
      bigEndian = false;
    }

    public BinaryReader(Stream stream, bool bigEndian) : base(stream)
    {
      this.bigEndian = bigEndian;
    }

    #endregion

    #region Public / Methods

    public static short ReverseOrder(short value)
    {
      return (short) ((value & 0xff) << 8 | (value >> 8 & 0xff));
    }

    public static int ReverseOrder(int value)
    {
      return ((ReverseOrder((short) value) & 0xffff) << 16 | (ReverseOrder((short) (value >> 16)) & 0xffff));
    }

    public static long ReverseOrder(long value)
    {
      unchecked
      {
        return (ReverseOrder((int) value) & (long) ((ulong) -1)) << 32 |
               ReverseOrder((int) (value >> 32)) & (long) ((ulong) -1);
      }
    }

    public static ushort ReverseOrder(ushort value)
    {
      return (ushort) ((value & 0xff) << 8 | (value >> 8 & 0xff));
    }

    public static uint ReverseOrder(uint value)
    {
      return (uint) ((ReverseOrder((ushort) value) & 0xffff) << 16 | (ReverseOrder((ushort) (value >> 16)) & 0xffff));
    }

    public static ulong ReverseOrder(ulong value)
    {
      unchecked
      {
        return (ReverseOrder((uint) value) & ((ulong) -1)) << 32 |
               ReverseOrder((uint) (value >> 32)) & ((ulong) -1);
      }
    }

    public int Peek()
    {
      if (Position + 1 >= BaseStream.Length)
        return -1;
      else
      {
        byte b = ReadByte();
        Seek(-1, SeekOrigin.Current);
        return b;
      }
    }

    public override int PeekChar()
    {
      return base.PeekChar();
    }

    public override int Read(char[] buffer, int index, int count)
    {
      return base.Read(buffer, index, count);
    }

    public override int Read()
    {
      return base.Read();
    }

    public override int Read(byte[] buffer, int index, int count)
    {
      return base.Read(buffer, index, count);
    }

    public override bool ReadBoolean()
    {
      return base.ReadBoolean();
    }

    public override byte ReadByte()
    {
      return base.ReadByte();
    }

    public override byte[] ReadBytes(int count)
    {
      return base.ReadBytes(count);
    }

    public override char ReadChar()
    {
      return base.ReadChar();
    }

    public override char[] ReadChars(int count)
    {
      return base.ReadChars(count);
    }

    public override decimal ReadDecimal()
    {
      return base.ReadDecimal();
    }

    public override double ReadDouble()
    {
      return base.ReadDouble();
    }

    public override short ReadInt16()
    {
      short value = base.ReadInt16();
      if (!bigEndian)
        return value;
      return ReverseOrder(value);
    }

    public override int ReadInt32()
    {
      int value = base.ReadInt32();
      if (!bigEndian)
        return value;
      return ReverseOrder(value);
    }

    public override long ReadInt64()
    {
      long value = base.ReadInt64();
      if (!bigEndian)
        return value;
      return ReverseOrder(value);
    }

    public override sbyte ReadSByte()
    {
      return base.ReadSByte();
    }

    public override float ReadSingle()
    {
      return base.ReadSingle();
    }

    public override string ReadString()
    {
      return base.ReadString();
    }

    public override ushort ReadUInt16()
    {
      ushort value = base.ReadUInt16();
      if (!bigEndian)
        return value;
      return ReverseOrder(value);
    }

    public override uint ReadUInt32()
    {
      uint value = base.ReadUInt32();
      if (!bigEndian)
        return value;
      return ReverseOrder(value);
    }

    public override ulong ReadUInt64()
    {
      ulong value = base.ReadUInt64();
      if (!bigEndian)
        return value;
      return ReverseOrder(value);
    }

    public void Seek(long offset, SeekOrigin origin)
    {
      BaseStream.Seek(offset, origin);
    }

    #endregion

    #region Protected / Methods

    protected override void FillBuffer(int numBytes)
    {
      base.FillBuffer(numBytes);
    }

    #endregion

    #region Private / Attributes

    private bool bigEndian;

    #endregion
  }
}